package com.news.cloud.service;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.news.cloud.exception.ServerTechnicalException;
import com.news.cloud.jpa.domain.Provider;
import com.news.cloud.jpa.domain.RssLink;
import com.news.cloud.jpa.repository.ProviderRepository;
import com.news.cloud.web.model.rss.Rss;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.transaction.Transactional;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;

/**
 * Created by hiephn on 2014/08/28.
 */
@Service
public class RssService extends AbstractService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RssService.class);

    private Unmarshaller _rssUnmarshaller;

    @Autowired
    private ProviderRepository providerRepository;
    @Autowired
    private FeedService feedService;

    @Transactional(rollbackOn = Exception.class)
    public void update() throws ServerTechnicalException {
        // get provider list
        List<Provider> providers = providerRepository.findAll();

        // loop on provider list, get rss link list
        providers.forEach(provider -> {
            RssLink firstPageRss = null;
            // loop on rss link list, get rss link
            for (RssLink rssLink : provider.getRssLinks()) {
                // save the first page RSS for later use
                if (rssLink.isFirstPage()) {
                    firstPageRss = rssLink;
                } else {
                    // get the rss from provider server, map to java object
                    Rss rss = null;
                    try {
                        rss = this.getRss(rssLink.getUrl());
                    } catch (Exception e) {
                        // catch all exceptions, so the corrupted rss is bypassed
                        LOGGER.error("Error while getting RSS: " + rssLink.getUrl(), e);
                    }
                    if (rss != null) {
                        // persist rss object
                        rss.getChannel().getItemList().forEach(item -> {
                            try {
                                if (isNotEmpty(item.getTitle())
                                        && isNotEmpty(item.getPubDate())
                                        && isNotEmpty(item.getLink())) {
                                    feedService.createWithUpdate(item.getTitle(), item.getDescription(), item.getLink(),
                                            item.getPubDate(), rssLink.getChannel(), provider);
                                }
                            } catch (ParseException e) {
                                // catch all exceptions, so the corrupted item is bypassed
                                LOGGER.error("Error while parsing rss", e);
                            }
                        });
                    }
                }
            }
            if (firstPageRss != null) {
                // get the rss from provider server, map to java object
                Rss rss = null;
                try {
                    rss = this.getRss(firstPageRss.getUrl());
                } catch (Exception e) {
                    // catch all exceptions, so the corrupted rss is bypassed
                    LOGGER.error("Error while getting first page RSS: " + firstPageRss.getUrl(), e);
                }
                if (rss != null) {
                    // reset feeds that is on first page
                    feedService.resetFirstPageFeed(provider.getId());
                    // loop on rss item to get feeds on first page
                    rss.getChannel().getItemList().forEach(item -> {
                        if (isNotEmpty(item.getTitle())
                                && isNotEmpty(item.getPubDate())
                                && isNotEmpty(item.getLink())) {
                            try {
                                feedService.makeFirstPage(item.getLink(), provider.getId(), item.getPubDate());
                            } catch (ParseException e) {
                                // catch all exceptions, so the corrupted item is bypassed
                                LOGGER.error("Error while parsing first page rss", e);
                            }
                        }
                    });
                }
            }
        });
    }

    protected Rss getRss(String rssLink) throws ServerTechnicalException, JAXBException {
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
        HttpEntity<String> stringHttpEntity = new HttpEntity<>("parameters",headers);
        ResponseEntity<String> responseEntity = restTemplate.exchange(
                rssLink, HttpMethod.GET, stringHttpEntity, String.class);
        String body = responseEntity.getBody();
            if (responseEntity.getStatusCode() == HttpStatus.OK) {
                Rss rss = (Rss) rssUnmarshaller().unmarshal(IOUtils.toInputStream(body));
                return rss;
            } else {
                throw new ServerTechnicalException(
                        String.format("Cannot read from %s. HTTP response status: %d. HTTP response body: %s",
                                rssLink, responseEntity.getStatusCode().value(), responseEntity.getBody()));
            }
    }

    private Unmarshaller rssUnmarshaller() throws JAXBException {
        if (_rssUnmarshaller == null) {
            _rssUnmarshaller = JAXBContext.newInstance(Rss.class).createUnmarshaller();
        }
        return _rssUnmarshaller;
    }
}
